'''
Created on 23/09/2010

@author: fccoelho
'''

import os
import pickle
import zlib
from django.utils import  simplejson as json
import md5

from google.appengine.ext import webapp
from google.appengine.ext.webapp import util
from google.appengine.ext.webapp.util import login_required
from google.appengine.api import users
from google.appengine.api import mail
from google.appengine.ext.webapp import template
from google.appengine.api import memcache

from models import *


openIdProviders = (
    ('Gmail.com','images/google.png'), # shorter alternative: "Gmail.com"
    ('Yahoo.com','images/yahoo.png'),
    ('MySpace.com','images/myspace.png'),
    ('AOL.com','images/aol.png'),
    ('Flickr.com','images/flickr.png'),
    ('Wordpress.com','images/wordpress.png')
    # add more here
)


class MainHandler(webapp.RequestHandler):
    '''main page'''
    def get(self):
        ms = Model.all().order('-last_updated').fetch(50)
        localized=0
        u = users.get_current_user()
        if u:
            rm=check_Member(u)
            if rm == None:#if user is not yet a member
                rm = Member(user=u)
                rm.put()
                
            else:
                localized = 1 if rm.location else 0
            ivs = OpenInvitation.all().filter('email =',rm.user.email())
            for i in ivs:
                if rm.key() not in i.model.shared_with:
                    i.model.shared_with.append(rm.key())
                    i.model.put() #add user to list of models collab.
                i.delete()#delete invitation
            url = users.create_logout_url(self.request.uri)
            url_linktext = 'Logout'
            logged_in = 1
            providers = []
            #TODO: add to members table if not there yet
        else:
            providers = [(users.create_login_url(dest_url="/",federated_identity=url[0].lower()),url[1]) for url in openIdProviders]
            url = users.create_login_url(self.request.uri)
            url_linktext = 'Login'
            logged_in = 0

        template_values = {
            'url': url,
            'url_linktext': url_linktext,
            'logged_in': logged_in,
            'providers':providers, #login urls and images
            'models':[(m,m.key()) for m in ms],
            'localized':localized,
            }
        
        path = os.path.join(os.path.dirname(__file__), 'templates/index.html')
        pag = memcache.get('frontpage',template.render(path, template_values))
        if pag ==None:
            pag = template.render(path, template_values)
            memcache.set('frontpage',pag, 300) #caches rendered page for 5 minutes
        self.response.out.write(template.render(path, template_values))
    
class Profile(webapp.RequestHandler):
    """
    Profile page
    """
    @login_required
    def get(self):
        if self.request.get('key'):
            k = db.Key(self.request.get('key'))
            u = db.get(k).user
        else:
            u = users.get_current_user()
        logged_in = 1
        mem = check_Member(u)
        localized = 0
        if mem:
            localized = 1 if mem.location else 0
        hash = md5.md5(u.email()).hexdigest()
        mq = Model.all().filter('author =',mem).order('name') #my models
        mods = [(m,m.key()) for m in mq]
        cols = []
        #TODO: include those which shared with mem in the collaborator list
        try:
            for m in mods:
                if m[0].shared_with:
                    cols.extend([(db.get(c),c) for c in m[0].shared_with])
        except AttributeError:
            #model has not yet been shared
            pass
        #models shared with mem
        mswmem = Model.gql("WHERE shared_with = :1", mem.key())
        for m in mswmem:
            cols.append((m.author,m.author.key()))
            
        markers = make_markers(u)
        paths = make_paths(u, cols)
        if mem:
            memid = mem.user.nickname() if mem.user.nickname() else mem.user.email()
        else:
            memid = u.nickname()
        template_values = {
                'user':memid,
                'logged_in':logged_in,
                'models':mods,
                'collaborators':cols,
                'hash':hash,
                'localized':localized,
                'markers':markers,
                'paths':paths,
                           }
        path = os.path.join(os.path.dirname(__file__), 'templates/profile.html')
        self.response.out.write(template.render(path, template_values))


class Invite(webapp.RequestHandler):
    @login_required
    def get(self):
        u = users.get_current_user()
        mb = check_Member(u)
        mkey = db.Key(self.request.get('mkey'))
        mo = db.get(mkey)
        cemails = self.request.get('collaborators').splitlines()
        for e in cemails:
            c = member_by_email(e)
            if c:
                if c.key() not in mo.shared_with:
                    mo.shared_with.append(c.key())
                    mo.put()
            else:
                oi = OpenInvitation(email=e,model=mo)
                oi.put()
            #send invite email
            self.send_invite_email(u, e, mkey)
        self.redirect('/profile')
            
    def send_invite_email(self,u, cem, mkey):
        if not mail.is_email_valid(cem):
            # prompt user to enter a valid address
            pass
        else:
            model_url = 'http://model-builder.appspot.com/model?key="%s"'%mkey
            sender_address = "%s <%s>"%(u.nickname(),u.email())
            subject = "A Model has been shared with you"
            body = """
            Hello!  User %s has shared a model with you. Click the link below to view it:
            
            <a href='%s'>link to model page</a>
            
            sincerely,
            
            The Model-Builder Cloud Community
            http://model-builder.appspot.com
            """ %(u.nickname(),model_url)
        mail.send_mail(u.email(), cem, subject, body)
    

class Library(webapp.RequestHandler):
    '''
    Model Library page
    '''
    def get(self):
        u = users.get_current_user()
        localized = 0
        mb = check_Member(u)
        if mb:
            localized = 1 if mb.location else 0
        logged_in = 1 if u else 0
        models = Model.all().order('name')
        template_values = {
                'models':[(m,m.key()) for m in models if m.public or mb in m.shared_with or mb == m.author],
                'logged_in':logged_in,
                'member':mb,
                'localized':localized,
                           }
        path = os.path.join(os.path.dirname(__file__), 'templates/library.html')
        self.response.out.write(template.render(path, template_values))

def check_Member(u):
    """
    return the user if it is in Member table
    """
    uem  = u.email()
    rm = memcache.get(uem)
    if rm is not None:
        return rm
    if u:
        mq = Member.all()
        for m in mq:
            if m.user.federated_identity() == u.federated_identity() or m.user.email() == u.email():
                rm = m
                memcache.add(rm.user.email(),rm)
                break 
    return rm

def member_by_email(em):
        """
        Return the member which has this email or None
        """
        record = None
        for i in Member.all():
            if i.user.email() == em:
                record = i
        return record

class ModelView(webapp.RequestHandler):
    '''
    Renders the model presentation page
    '''
    @login_required
    def get(self):
        u = users.get_current_user()
        mb = check_Member(u)
        localized = 0
        if mb:
            localized = 1 if mb.location else 0
        mkey = db.Key(self.request.get('key'))
        model = db.get(mkey)
        eqs = json.loads(model.latex)
        pars = json.loads(model.mjson)['parameters'].splitlines()
        comm = []
        try:
            comm = [db.get(k) for k in model.shared_with]
        except AttributeError:
            comm = []
        template_values = {
            'modelname': model.name,
            'author': model.author,
            'mkey':model.key(),
            'eqlist':[e.strip('$') for e in eqs],
            'parlist':pars,
            'community':comm,
            'member':mb,
            'localized':localized,
                           }
        path = os.path.join(os.path.dirname(__file__), 'templates/model.html')
        self.response.out.write(template.render(path, template_values))
        
    def post(self):
        '''
        Handle model uploads
        '''
        u = users.User(self.request.get('author'))
        mn = self.request.get('name')
        aut = Member.all().filter('user =', u).get()
        src = self.request.get('source')
        pub = bool(int(self.request.get('public')))
        mo = Model.all().filter('author =', aut).filter('name =', mn).get()
        modi = self.request.get('modict')
        leqs = self.request.get('leqs')
        if not mo:
            mo = Model(name=mn,author=aut,source=src,public=pub,mjson=modi,latex=leqs)
        else:
            mo.name = mn
            mo.author = aut
            mo.source = src
            mo.public = pub
        mo.put()
        
class Monitor(webapp.RequestHandler):
    """
    Handles all Model builder usage reporting
    """
    def post(self):
        if self.request.get('event') == 'install':
            inst = Install(version=self.request.get('version'),
                           opsys = self.request.get('opsys')
                           )
            inst.put()
            #print inst.version
        elif self.request.get('event') == 'session':
            m = Member.all().filter('user =', users.User(self.request.get('userid'))).get()            
            se = Session(
            member = m,
            duration = int(float(self.request.get('duration')))
            )
            se.put()
            mo = Model.all().filter('name =',self.request.get('model')).filter('author =',m).get()
            if mo:
                se.model = mo
            se.put()

class About(webapp.RequestHandler):
    def get(self):
        u = users.get_current_user()
        localized = 0
        if u:
            mb = check_Member(u)
            localized = 1 if mb.location else 0
        logged_in = 1 if u else 0
        ninst = Install.all().count()
        finst = Install.all().order('date').get()
        nusers = Member.all().count()
        huse = sum([s.duration for s in Session.all()])/3600.
        markers = make_markers(u)

        if not finst:
            fdate =None
        else:
            fdate = finst.date
        
        template_values = {
            'ninst': ninst,
            'fdate': fdate,
            'nusers': nusers,
            'huse': huse,
            'logged_in':logged_in,
            'localized':localized,
            'markers':markers,
            }

        path = os.path.join(os.path.dirname(__file__), 'templates/about.html')
        self.response.out.write(template.render(path, template_values))

class Verify(webapp.RequestHandler):
    '''Returns 1 or 0 according to whether the userid is registered''' 
    def post(self):
        id = self.request.get('userid')
        u = users.User(id)
        M = Member.all().filter('user =',u).get()
        if M:
            self.response.out.write('1')
            return
        self.response.out.write('0')
        
class SetLocation(webapp.RequestHandler):
    def post(self):
        u = users.get_current_user()
        if not u:
            return
        m = Member.all().filter('user =', u).get()
        if not m: #user is not registered
            return
        lat = float(self.request.get('lat'))
        lon = float(self.request.get('lon'))
        m.location = db.GeoPt(lat,lon)
        m.put()

class ModelTstamp(webapp.RequestHandler):
    def post(self):
        id = self.request.get('userid')
        u = users.User(id)
        A = Member.all().filter('user =',u).get()
        mn = self.request.get('modelname')
        M = Model.all().filter('author =',A).filter('name =', mn).get()
        if M:
            self.response.out.write(M.last_updated.isoformat())
        else:
            self.response.out.write('0')
        
class DownloadModel(webapp.RequestHandler):
    @login_required
    def get(self):
        u = users.get_current_user()
        mb = check_Member(u)
        mkey = db.Key(self.request.get('key'))
        m = db.get(mkey)
        if not m.public or mb in m.shared_with or m.author == mb:
            self.redirect('/')
        src = m.source
        self.response.headers['Content-Type'] = "application/x-download"
        self.response.headers['Content-Length'] = str(len(src))
        self.response.headers["Content-Disposition"] = "attachment; filename=%s"%m.name
        self.response.out.write(src)    


def make_markers(u):
    '''
    creates a list of markers to use with google maps static api
    '''
    markers = ""
    mb = check_Member(u)
    if u:
        loc = mb.location
        markers += "&markers=color:blue|%s,%s"%(loc.lat,loc.lon)
    mq = Member.all()
    for m in mq:
        if m == mb: continue
        try:
            loc = m.location
            markers += "&markers=color:green|%s,%s"%(loc.lat,loc.lon)
        except:
            pass
    return markers

def make_paths(u,cols):
    """
    Creates a list of paths connecting a user to his collaborators.
    """
    paths = ""
    mb = check_Member(u)
    if not mb:
        return paths
    l1 = mb.location
    for c in cols:
        try:
            l2 = c[0].location
            paths += "&path=color:gray|%s,%s|%s,%s"%(l1.lat,l1.lon,l2.lat,l2.lon)
        except AttributeError:
            pass
    return paths
        



def main():
    application = webapp.WSGIApplication([('/', MainHandler),
                                          ('/profile', Profile),
                                          ('/monitor', Monitor),
                                          ('/library',Library),
                                          ('/about', About),
                                          ('/verify',Verify),
                                          ('/modelview',ModelView),
                                          ('/setlocation',SetLocation),
                                          ('/model',ModelView),
                                          ('/model/timestamp',ModelTstamp),
                                          ('/model/download',DownloadModel),
                                          ('/invite', Invite),],
                                         debug=False)
    util.run_wsgi_app(application)
    
if __name__ == '__main__':
    pass